package com.hy.net.flow

import com.hy.net.error.ApiException
import com.hy.net.error.ERROR
import com.hy.net.error.ExceptionHandler
import com.hy.net.response.BaseResponse
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.onCompletion
import kotlinx.coroutines.flow.onStart
import kotlinx.coroutines.withTimeout

/**
 * @author mingyan.su
 * @date   2023/4/12 22:47
 * @desc   flow请求拓展
 */

/**
 * 通过flow执行请求，需要在协程作用域中执行
 * @param errorBlock 错误回调
 * @param requestCall 执行的请求
 * @param showLoading 开启和关闭加载框
 * @return 请求结果
 */
suspend fun <T> requestFlow(
    errorBlock: ((Int?, String?, BaseResponse<T>?) -> Unit)? = null,
    exceptionHandler: ExceptionHandler,
    requestCall: suspend () -> BaseResponse<T>?,
    showLoading: ((Boolean) -> Unit)? = null,
    flowTimeOut: Long = 10 * 1000
): T? {
    var data: T? = null
    val flow = requestFlowResponse(
        errorBlock,
        exceptionHandler,
        requestCall,
        showLoading,
        flowTimeOut
    )
    //7.调用collect获取emit()回调的结果，就是请求最后的结果
    flow.collect {
        data = it?.data
    }
    return data
}

/**
 * 通过flow执行请求，需要在协程作用域中执行
 * @param errorBlock 错误回调
 * @param requestCall 执行的请求
 * @param showLoading 开启和关闭加载框
 * @return Flow<BaseResponse<T>>
 */
suspend fun <T> requestFlowResponse(
    errorBlock: ((Int?, String?, BaseResponse<T>?) -> Unit)? = null,
    exceptionHandler: ExceptionHandler,
    requestCall: suspend () -> BaseResponse<T>?,
    showLoading: ((Boolean) -> Unit)? = null,
    flowTimeOut: Long
): Flow<BaseResponse<T>?> {
    //1.执行请求
    val flow = flow {
        //设置超时时间
        val response = withTimeout(flowTimeOut) {
            requestCall()
        }

        if (response?.isFailed() == true) {
            //失败的情况下要把response也回调回去，业务有可能会用到response的数据
            errorBlock?.invoke(response.errorCode, response.errorMsg, response)
            //发送一个唯一标识code，catch中就不再回调了
            throw ApiException(ERROR.BUSINESS_FAILED.code, ERROR.BUSINESS_FAILED.errMsg)
        }
        //2.发送网络请求结果回调
        emit(response)
        //3.指定运行的线程，flow {}执行的线程
    }.flowOn(Dispatchers.IO)
        .onStart {
            //4.请求开始，展示加载框
            showLoading?.invoke(true)
        }
        //5.捕获异常
        .catch { e ->
            e.printStackTrace()
            if (e is ApiException && e.errCode == ERROR.BUSINESS_FAILED.code) {
                return@catch
            }

            val exception = exceptionHandler.handleException(e)
            errorBlock?.invoke(exception.errCode, exception.errMsg, null)
        }
        //6.请求完成，包括成功和失败
        .onCompletion {
            showLoading?.invoke(false)
        }
    return flow
}